package com.alks.function.service.impl.produce;

import com.alks.common.aop.AutoPageAop;
import com.alks.common.data.dto.SysUserTokenDTO;
import com.alks.common.data.thread.UserIdThread;
import com.alks.common.service.RedisService;
import com.alks.common.utils.beanutils.ArrayUtils;
import com.alks.common.utils.beanutils.BeanUtil;
import com.alks.common.utils.stringUtils.StringFormatUtils;
import com.alks.entity.data.entity.*;
import com.alks.entity.data.entity.dispatch.CuttingDispatchHead;
import com.alks.entity.data.page.PageParam;
import com.alks.entity.data.response.ResponseInfo;
import com.alks.entity.exception.ServiceErrorException;
import com.alks.function.data.dto.produce.DispatchOtherTempDto;
import com.alks.function.data.dto.produce.DispatchTempDto;
import com.alks.function.data.dto.produce.SuppDispatchPrintDto;
import com.alks.function.data.request.produce.*;
import com.alks.function.mapper.administration.PcEmployeeMapper;
import com.alks.function.mapper.administration.PcEmployeeWorkTypeMapper;
import com.alks.function.mapper.chengKong.PcLotHeadMapper;
import com.alks.function.mapper.finance.salary.PcDispatchCfmDetailMapper;
import com.alks.function.mapper.pcfactorywork.PcSupplementHeadMapper;
import com.alks.function.mapper.produce.CuttingDispatchHeadMapper;
import com.alks.function.mapper.produce.PcCuttingDispatchOtherDMapper;
import com.alks.function.mapper.produce.PcCuttingDispatchOtherMapper;
import com.alks.function.mapper.produce.PcDispatchCollectionHeadMapper;
import com.alks.function.service.produce.ISuppDispatchService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.Page;
import jodd.util.StringUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.alks.entity.data.enums.RedisKeyEnum.SUPP_DISPATCH;

/**
 * @author MS
 * @since 2024/7/3上午11:52
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class SuppDispatchServiceImpl extends ServiceImpl<PcCuttingDispatchOtherMapper, PcCuttingDispatchOther> implements ISuppDispatchService {
    private final PcCuttingDispatchOtherMapper otherMapper;
    private final PcCuttingDispatchOtherDMapper otherDMapper;
    private final PcEmployeeMapper employeeMapper;
    private final PcSupplementHeadMapper supplementHeadMapper;
    private final RedisService redisService;
    private final PcDispatchCollectionHeadMapper headMapper;
    private final CuttingDispatchHeadMapper cutHeadMapper;
    private final PcEmployeeWorkTypeMapper employeeWorkTypeMapper;
    private final PcDispatchCfmDetailMapper cfmDetailMapper;
    private final PcLotHeadMapper lotHeadMapper;

    @Override
    @AutoPageAop
    public ResponseInfo suppDispatchHeadQuery(DisOthHeadRequest request, String processCode) {
        String companyId = UserIdThread.get().getCompanyId();
        /*处理字段*/
        request.setOrdName(StringFormatUtils.elementToTable(request.getOrdName()));
        /*获取数据*/
        List<DispatchOtherTempDto> dtos = otherMapper.dispatchHeadList(request, companyId, processCode);
        Page<DispatchOtherTempDto> page = (Page<DispatchOtherTempDto>) dtos;
        /*封装返回*/
        Map<String, Object> map = new HashMap<>();
        map.put("dtos", dtos);
        map.put("total", page.getTotal());
        return ResponseInfo.ok(map);
    }

    @Override
    public ResponseInfo<List<Map<String, Object>>> suppDispatchDetailQuery(DispatchOtherFindRequest request, String processCode) {
        String companyId = UserIdThread.get().getCompanyId();
        /*获取数据*/
        List<Map<String, Object>> dtos = otherDMapper.dispatchDetailQuery(request, processCode, companyId);
        return ResponseInfo.ok(dtos);
    }

    @Override
    public Map<String, Map<String, List<DispatchTempDto>>> suppDispatchTempQuery(DisOthTempRequest request, String processCode) {
        String companyId = UserIdThread.get().getCompanyId();
        /*获取数据*/
        List<DispatchTempDto> tempVos = otherMapper.dispatchTempQuery(request, processCode, companyId);
        if (ArrayUtils.isEmpyt(tempVos)) {
            return null;
        }
        /*根据lotNo分组*/
        Map<String, List<DispatchTempDto>> lotGroup = tempVos.stream()
                .collect(Collectors.groupingBy(DispatchTempDto::getLotNo));
        /*根据process-partName分组*/
        Map<String, Map<String, List<DispatchTempDto>>> dto = lotGroup.entrySet().stream()
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        entry -> entry.getValue().stream()
                                .collect(Collectors.groupingBy(a -> a.getProcess() + "-" + a.getPartName().substring(a.getProcess().length())))
                ));
        return dto;
    }

    @Override
    public ResponseInfo suppDispatchPrintQuery(SuppDispatchPrintRequest request, String processCode) {
        String companyId = UserIdThread.get().getCompanyId();
        /*获取框架数据*/
        SuppDispatchPrintDto dto = BeanUtil.copyBean(request, SuppDispatchPrintDto.class);
        List<String> detail = new ArrayList<>();
        List<List<String>> details = new ArrayList<>();
        detail.add("工艺");
        detail.add("部位");
        /*获取明细数据*/
        LambdaQueryWrapper<PcCuttingDispatchOtherD> wrapper = new LambdaQueryWrapper<PcCuttingDispatchOtherD>()
                .eq(PcCuttingDispatchOtherD::getDispatchType, request.getDispatchType())
                .eq(PcCuttingDispatchOtherD::getLotNo, request.getLotNo())
                .eq(PcCuttingDispatchOtherD::getLsNo, request.getLsNo())
                .eq(PcCuttingDispatchOtherD::getWorkerName, request.getWorkerName())
                .eq(PcCuttingDispatchOtherD::getDispatchDate, request.getDispatchDate())
                .eq(PcCuttingDispatchOtherD::getCompanyId, companyId);
        List<PcCuttingDispatchOtherD> otherDs = otherDMapper.selectList(wrapper);
        /*获取号码数*/
        List<String> sizeNos = otherDs.stream()
                .filter(Objects::nonNull)
                .map(PcCuttingDispatchOtherD::getSizeNo)
                .distinct()
                .sorted(Comparator.comparingDouble(Double::parseDouble))
                .collect(Collectors.toList());
        detail.addAll(sizeNos);
        detail.add("合计");
        details.add(detail);
        /*处理明细数据*/
        List<String> processParts = otherDs.stream()
                .filter(Objects::nonNull)
                .sorted(Comparator.comparing(PcCuttingDispatchOtherD::getProcess))
                .map(a -> a.getProcess() + "_" + a.getPartName())
                .distinct()
                .collect(Collectors.toList());
        //获取部位+号码为键，数量为值Map方便补充数据
        Map<String, BigDecimal> partSizeToQtyMap = otherDs.stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toMap(a -> a.getPartName() + "_" + a.getSizeNo(), PcCuttingDispatchOtherD::getQty, BigDecimal::add));
        for (String part : processParts) {
            String[] split = part.split("_");
            detail = new ArrayList<>(Arrays.asList(split));
            //获取合计
            BigDecimal sum = BigDecimal.ZERO;
            for (String sizeNo : sizeNos) {
                BigDecimal qty = partSizeToQtyMap.get(detail.get(1) + "_" + sizeNo);
                qty = qty == null ? BigDecimal.ZERO : qty.setScale(1, RoundingMode.HALF_UP);
                detail.add(qty.toString());
                sum = sum.add(qty).setScale(1, RoundingMode.HALF_UP);
            }
            detail.add(sum.toString());
            details.add(detail);
        }
        dto.setDetail(details);
        return ResponseInfo.ok(dto);
    }

    /**
     * 短期测试用
     *
     * @return 测试用数据
     */
    private static List<DispatchTempDto> createSampleData() {
        List<DispatchTempDto> data = new ArrayList<>();
        data.add(new DispatchTempDto("指令1", "补单号", "冲裁", "CUT001", "S", "001", new BigDecimal("10"), "冲裁鞋身"));
        data.add(new DispatchTempDto("指令1", "补单号", "冲裁", "CUT001", "M", "002", new BigDecimal("20"), "冲裁鞋头"));
        data.add(new DispatchTempDto("指令1", "补单号", "冲裁", "CUT001", "L", "003", new BigDecimal("15"), "冲裁鞋身"));
        data.add(new DispatchTempDto("指令1", "补单号", "高频", "SEW002", "S", "001", new BigDecimal("12"), "高频鞋头"));
        data.add(new DispatchTempDto("指令1", "补单号", "高频", "SEW002", "M", "002", new BigDecimal("18"), "高频鞋身"));
        data.add(new DispatchTempDto("指令1", "补单号", "高频", "SEW002", "L", "003", new BigDecimal("13"), "高频鞋头"));
        data.add(new DispatchTempDto("指令2", "补单号", "手工", "EMB003", "S", "001", new BigDecimal("11"), "手工鞋身"));
        data.add(new DispatchTempDto("指令2", "补单号", "手工", "EMB003", "M", "002", new BigDecimal("19"), "手工鞋头"));
        data.add(new DispatchTempDto("指令2", "补单号", "手工", "EMB003", "L", "003", new BigDecimal("14"), "手工鞋身"));
        return data;
    }

    @Override
    public ResponseInfo suppDispatchEmpQuery(String searchKey, String processCode) {
        String companyId = UserIdThread.get().getCompanyId();
        /*获取工种类型*/
        List<String> workType = employeeWorkTypeMapper.getHandType(companyId);
        workType.add("冲工");
        workType.add("点检");
        /*获取员工明细*/
        LambdaQueryWrapper<PcEmployee> wrapper = new LambdaQueryWrapper<PcEmployee>()
                .select(PcEmployee::getName, PcEmployee::getDeptNo, PcEmployee::getDeptName,
                        PcEmployee::getWorkType, PcEmployee::getWorkerId, PcEmployee::getIdNo)
                .and(StringUtil.isNotBlank(searchKey),w->w
                    .like(PcEmployee::getName, searchKey)
                    .or().like(PcEmployee::getWorkerId, searchKey)
                )
                .in(PcEmployee::getWorkType, workType)
                .eq(PcEmployee::getStatus, 1)
                .eq(PcEmployee::getCompanyId, companyId)
                .last("order by DEPT_NO desc,worker_id desc");
        /*处理数据格式返回*/
        Map<String, List<Map<String, Object>>> dtos = employeeMapper.selectList(wrapper)
                .stream()
                .filter(Objects::nonNull)
                /*根据部门ID分组为键 内容是包括的成员信息*/
                .collect(Collectors.groupingBy(
                        e -> "部门：" + e.getDeptName(),
                        Collectors.mapping(
                                e -> {
                                    Map<String, Object> map = new HashMap<>(3);
                                    map.put("workerId", e.getWorkerId());
                                    map.put("workerIdNo", e.getIdNo());
                                    map.put("name", e.getName());
                                    map.put("workType", e.getWorkType());
                                    map.put("deptName", e.getDeptName());
                                    map.put("deptNo", e.getDeptNo());
                                    return map;
                                },
                                Collectors.toList()
                        )
                ));
        return ResponseInfo.ok(dtos);
    }

    @Override
    @AutoPageAop
    public ResponseInfo suppDispatchSlipNoBack(String slipNo, PageParam request, String processCode) {
        String companyId = UserIdThread.get().getCompanyId();
        /*获取数据*/
        LambdaQueryWrapper<PcSupplementHead> wrapper = new LambdaQueryWrapper<PcSupplementHead>()
                .select(PcSupplementHead::getSlipNo)
                .like(StringUtil.isNotBlank(slipNo), PcSupplementHead::getSlipNo, slipNo)
                .eq(PcSupplementHead::getCompanyId, companyId)
                .last("ORDER BY SYS_DATE DESC");
        List<PcSupplementHead> heads = supplementHeadMapper.selectList(wrapper);
        List<String> slipNos = heads
                .stream().filter(Objects::nonNull)
                .map(PcSupplementHead::getSlipNo)
                .collect(Collectors.toList());
        Map<String, Object> map = new HashMap<>(2);
        map.put("slipNos", slipNos);
        if (request.getPageSize() != -1 && request.getPageNum() != -1) {
            Page<PcSupplementHead> page = (Page<PcSupplementHead>) heads;
            map.put("total", page.getTotal());
        }else{
            map.put("total", slipNos.size());
        }
        /*封装返回*/
        return ResponseInfo.ok(map);
    }

    @Override
    public List<String> suppDispatchTempProcessBack(String slipNo, String process, String processCode) {
        int processMax = 10;
        String companyId = UserIdThread.get().getCompanyId();
        /*获取数据*/
        Set<String> processList = new HashSet<>();
        List<SdStyleProcessNew> processes = otherMapper.dispatchTempProcessBack(slipNo, process, processCode, companyId);
        Set<String> unProcess = otherMapper.fullProcessQuery(slipNo,process,processCode,companyId);
        /*获取返回数据*/
        for (SdStyleProcessNew processNew : processes) {
            for (int i = 1; i <= processMax; i++) {
                try {
                    Method method = processNew.getClass().getMethod("getProcess" + i);
                    String processValue = (String) method.invoke(processNew);
                    if (!unProcess.contains(processValue)) {
                        processList.add(processValue);
                    }
                } catch (Exception e) {
                    log.error("\n错误接口：suppDispatchTempProcessBack\n错误原因：反射调用异常", e);
                    throw new ServiceErrorException("数据获取失败");
                }
            }
        }
        return new ArrayList<>(processList);
    }

    @Override
    public ResponseInfo suppDispatchTempSizeBack(String slipNo, String sizeNo, String processCode) {
        String companyId = UserIdThread.get().getCompanyId();
        /*获取数据*/
        List<String> sizeNos = otherMapper.dispatchTempSizeBack(slipNo, sizeNo, processCode, companyId);
        return ResponseInfo.ok(sizeNos);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String suppDispatchAdd(SuppDispatchRequest request, String processCode) {
        SysUserTokenDTO tokenDTO = UserIdThread.get();
        String companyId = tokenDTO.getCompanyId();
        String loginName = tokenDTO.getLoginName();
        /*判断补单数据是否存在*/
        LambdaQueryWrapper<PcSupplementHead> suppWrapper = new LambdaQueryWrapper<PcSupplementHead>()
                .eq(PcSupplementHead::getSlipNo, request.getSlipNo())
                .eq(PcSupplementHead::getCompanyId, companyId);
        if (supplementHeadMapper.selectCount(suppWrapper) == 0) {
            throw new ServiceErrorException("补单号不存在");
        }
        /*判断员工是否存在*/
        List<String> workType = employeeWorkTypeMapper.getHandType(companyId);
        workType.add("冲工");
        workType.add("点检");
        LambdaQueryWrapper<PcEmployee> empolyeeWrapper = new LambdaQueryWrapper<PcEmployee>()
                .eq(PcEmployee::getName, request.getName())
                .eq(PcEmployee::getWorkerId, request.getWorkerId())
                .eq(PcEmployee::getIdNo, request.getWorkerIdNo())
                .in(PcEmployee::getWorkType, workType)
                .eq(PcEmployee::getCompanyId, companyId);
        if (employeeMapper.selectCount(empolyeeWrapper) == 0) {
            throw new ServiceErrorException("员工不存在 或 不是手工、冲裁、削皮员工");
        }
        /*判断是否是重复派工*/
        PcCuttingDispatchOtherD rptOther = otherMapper.rptDispatchQuery(request.getTemps(), companyId);
        if (!BeanUtil.isEmpty(rptOther)) {
            log.warn("\n异常接口：suppDispatchAdd\n异常原因：重复派工\n重复数据：{}", rptOther);
            throw new ServiceErrorException("存在已派工数据，请勿重复派工");
        }
        /*生成派工单号*/
        String dispatchType = redisService.slipNoCreat("", SUPP_DISPATCH.getKey(), SUPP_DISPATCH.getLength());
        /*生成补充用数据*/
        List<PcCuttingDispatchOtherD> otherDs = BeanUtil.copyList(request.getTemps(), PcCuttingDispatchOtherD.class);
        List<PcCuttingDispatchOther> others = new ArrayList<>();
        for (PcCuttingDispatchOtherD otherD : otherDs) {
            /*补全数据*/
            otherD.setCompanyId(companyId);
            otherD.setDispatchDate(request.getDispatchDate());
            otherD.setWorkerId(request.getWorkerId());
            otherD.setWorkerIdNo(request.getWorkerIdNo());
            otherD.setWorkerName(request.getName());
            otherD.setLsNo(BigDecimal.ONE);
            otherD.setLsNo2(BigDecimal.ONE);
            otherD.setProcessCode(processCode);
            otherD.setPartName0(otherD.getPartName().substring(otherD.getProcess().length()));
            otherD.setDispatchType(dispatchType);
            otherDMapper.insert(otherD);
            PcCuttingDispatchOther other = BeanUtil.copyBean(otherD, PcCuttingDispatchOther.class);
            other.setDispatchQty(otherD.getQty());
            other.setUserId(loginName);
            other.setDispatchName(loginName);
            other.setSysDate(LocalDateTime.now());
            other.setDispatchDept(request.getSlipNo());
            others.add(other);
        }
        /*获取分组后派工数*/
        Map<String, BigDecimal> groupSum = others.stream().collect(Collectors.toMap(other -> other.getLotNo() + other.getPartName(), PcCuttingDispatchOther::getDispatchQty, BigDecimal::add));
        /*获取分组数据*/
        Map<String, PcCuttingDispatchOther> otherMap = others.stream()
                .collect(Collectors.toMap(
                        o -> o.getLotNo() + o.getPartName(),
                        Function.identity(),
                        (o1, o2) -> o1
                ));
        otherMap.forEach((k, v) -> {
            v.setDispatchQty(groupSum.get(k));
            otherMapper.insert(v);
        });
        return request.getSlipNo();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseInfo suppDispatchDelete(List<PcCuttingDispatchOther> request, String processCode) {
        SysUserTokenDTO tokenDTO = UserIdThread.get();
        String companyId = tokenDTO.getCompanyId();
        String loginName = tokenDTO.getLoginName();
        /*获取数据*/
        for (PcCuttingDispatchOther r : request) {
            LambdaQueryWrapper<PcCuttingDispatchOther> otherWrapper = new LambdaQueryWrapper<PcCuttingDispatchOther>()
                    .eq(PcCuttingDispatchOther::getDispatchDate, r.getDispatchDate())
                    .eq(PcCuttingDispatchOther::getDispatchType, r.getDispatchType())
                    .eq(PcCuttingDispatchOther::getDispatchDept, r.getDispatchDept())
                    .eq(PcCuttingDispatchOther::getLotNo, r.getLotNo())
                    .eq(PcCuttingDispatchOther::getPartName, r.getPartName())
                    .eq(PcCuttingDispatchOther::getWorkerName, r.getWorkerName())
                    .eq(PcCuttingDispatchOther::getProcessCode, processCode)
                    .eq(PcCuttingDispatchOther::getCompanyId, companyId)
                    .last("LIMIT 1");
            PcCuttingDispatchOther other = otherMapper.selectOne(otherWrapper);
            if (BeanUtil.isEmpty(other)) {
                return ResponseInfo.error("无效派工记录");
            }
            /*隔天不能删*/
            if (!other.getDispatchDate().equals(LocalDate.now().atStartOfDay())) {
                return ResponseInfo.error("不能删除非当天派工记录");
            }
            /*非本人不可删*/
            if (!Objects.equals(other.getUserId(), loginName)) {
                return ResponseInfo.error("非本人数据不能删除");
            }
            /*已采集不能删*/
            LambdaQueryWrapper<PcDispatchCollectionHead> headWrapper = new LambdaQueryWrapper<PcDispatchCollectionHead>()
                    .eq(PcDispatchCollectionHead::getDispatchNo, other.getDispatchDept())
                    .eq(PcDispatchCollectionHead::getCompanyId, companyId);
            if (headMapper.selectCount(headWrapper) != 0) {
                return ResponseInfo.error("订单已采集，不能删除");
            }
            LambdaQueryWrapper<PcCuttingDispatchOtherD> otherDwrapper = new LambdaQueryWrapper<PcCuttingDispatchOtherD>()
                    .eq(PcCuttingDispatchOtherD::getDispatchDate, r.getDispatchDate())
                    .eq(PcCuttingDispatchOtherD::getDispatchType, r.getDispatchType())
                    .eq(PcCuttingDispatchOtherD::getLotNo, r.getLotNo())
                    .eq(PcCuttingDispatchOtherD::getPartName, r.getPartName())
                    .eq(PcCuttingDispatchOtherD::getWorkerName, r.getWorkerName())
                    .eq(PcCuttingDispatchOtherD::getProcessCode, processCode)
                    .eq(PcCuttingDispatchOtherD::getCompanyId, companyId);
            /*判断是否流入件资结算*/
            CuttingDispatchHead cuttingHead = new CuttingDispatchHead();
            cuttingHead.setCompanyId(companyId);
            cuttingHead.setPartName(r.getPartName());
            cuttingHead.setPlanNo(r.getDispatchDept());
            cuttingHead.setLotNo(r.getLotNo());
            cuttingHead.setLsNo(r.getLsNo().intValue());
            cuttingHead.setWorkerName(r.getWorkerName());
            int i = cutHeadMapper.queryByDispatchNoAndPartName(cuttingHead);
            if (i != 0) {
                return ResponseInfo.error("该订单已流入结算，不能删除");
            }
            /*删除对应数据*/
            otherDMapper.delete(otherDwrapper);
            otherMapper.delete(otherWrapper);
        }
        return ResponseInfo.ok();
    }

}
